#include <iostream>
#include <string>
#include <memory>
#include <mutex>

/** lambda表达式相关面试题
 * 1.lambda表达式在类的成员函数中可以访问这个类的私有成员吗
 * 2.lambda表达式可以拷贝吗
 * 3.lambda表达式按值捕获变量a,再拷贝这个lambda表达式是什么结果
 */

 // 1.lambda表达式在类的成员函数中可以访问这个类的私有成员吗，如何访问，如何传参
 // -- 可以   因为lambda表达式是类的内部的，所以可以访问， 只要捕获this指针就可以了
namespace S1 {
    class AnyClass {
    private:
        int private_data = 42;
    public:
        void func() {
            // 捕获this指针就可以了
            auto lambda = [this](){
                return private_data; // 直接访问私有成员
            }; // 为什么捕获this指针就可以了，因为这个lambda表达式是在类的作用域中 ，所以可以访问
            std::cout << lambda() << std::endl;
        }
    };

    class Bank;
    class BankAccount {
    private:
        double balance;
        static int totalAccounts;

    protected:
        std::string ownerName;

    public:
        BankAccount(std::string name, double initial): ownerName(name), balance(initial) {
            totalAccounts++;
        }

        void deposit(double amount) {
            balance += amount;
        }

        // 同类对象访问演示： 可以访问其他同类对象的私有成员
        bool transferTo(BankAccount &other, double amount) {
            other.balance+= amount; // 访问同类对象的私有成员
            return true;
        } // 如果这个函数在类的外部的话，只能访问public成员了

        // 静态成员函数， --- 在类的作用域内
        static bool compareBalance(const BankAccount& a, const BankAccount& b) {
            return a.balance > b.balance;
            // 静态函数通过对象参数访问私有成员
        }

        friend class Bank; // 友元是可以访问私有成员
    };

     // 继承关系只能访问public 和 protected    
}

namespace S2 {
    // 2.lambda表达式可以拷贝吗
    // 可以，但是有限制条件， ，， 取决于捕获的对象是否可以拷贝的

    // 可以拷贝的情况
    void func1() {
        int x = 10;
        // 值捕获 - 可拷贝
        auto lambda1 = [x]() {return x + 1;};
        auto copy1 = lambda1;

        int y = 20;
        // 引用捕获 - 可拷贝(拷贝的是引用本身)
        auto lambda2 = [&y]() {return y+ 1;};
        auto copy2 = lambda2;  // 可以拷贝
    }

    class NonCopyable {
    public:
        NonCopyable() = default;
        NonCopyable(const NonCopyable&) = delete;
        NonCopyable& operator=(const NonCopyable&) = delete;
    };

    void func2() {
         std::unique_ptr<int> ptr = std::make_unique<int>(42);
         std::mutex mtx;

         /**  都是不可拷贝的
         auto lambda3 = [ptr](){ //编译错误
            return *ptr;
         };
         auto lambda4 = [p = std::move(ptr)]() { reutrn *p;}; // 移动捕获 -- 生成的lambda不可以拷贝
         auto copy4 = lambda4;
         auto lambda5 = [mtx](){} //编译错误
         */
        // 正确的方式是：使用引用捕获
        auto lambda6 = [&ptr](){ return *ptr;};
        auto copy6 = lambda6;

        NonCopyable nc;
        // auto lambda7 = [nc](){}; // 编译错误，捕获的对象不可拷贝
    }
}

namespace S3 {
    // 3.lambda表达式按值捕获变量a,再拷贝这个lambda表达式是什么结果
    // 有两个独立的lambda对象，各自拥有变量a的副本   
    // 如果是按引用捕获的话， 就会一起变化
    void func() {
        int counter = 0;
        
        auto counter_lambda = [&counter]() mutable {  // 成员函数用const 修饰this指针 不能修改counter
            counter ++; // 按值捕获  必须添加 mutable 才能进行修改
            return counter;
        };

         auto copy1 = counter_lambda;
         auto copy2 = counter_lambda;

         std::cout << copy1() << std::endl; // 输出1
         std::cout << copy1() << std::endl; // 输出2
         std::cout << copy2() << std::endl; // 输出1
         std::cout << copy2() << std::endl; // 输出2
         std::cout << counter_lambda() << std::endl; // 输出1
    }
}

 
int main() {
    S3::func();
    return 0;
}